#ifndef BINARY_SEARCH_TREE_H
#define BINARY_SEARCH_TREE_H

#include <iostream>
#include <algorithm>
using namespace std;       


template <typename Comparable>
class BinarySearchTree
{
  public:

    BinarySearchTree( ) : root{ nullptr } 
    {  }


    BinarySearchTree( const BinarySearchTree & rhs ) : root{ nullptr }
    {
		root = clone( rhs.root );
    }


    BinarySearchTree( BinarySearchTree && rhs ) : root{ rhs.root }
    {
		rhs.root = nullptr;
    }
    

    ~BinarySearchTree( )
    {
        makeEmpty( );
    }


    BinarySearchTree & operator=( const BinarySearchTree & rhs )
    {
        BinarySearchTree copy = rhs;
        std::swap( *this, copy );
        return *this;
    }
        

    BinarySearchTree & operator=( BinarySearchTree && rhs )
    {
        std::swap( root, rhs.root );	
        return *this;
    }
    
    

    const Comparable & findMin( ) const
    {
        if( isEmpty( ) )
	return 0;
        return findMin( root )->element;
    }


    const Comparable & findMax( ) const
    {
        if( isEmpty( ) )
	return 0;
	
        return findMax( root )->element;
    }


    bool contains( const Comparable & x ) const
    {
        return contains( x, root );

    }


    bool isEmpty( ) const
    {
        return root == nullptr;
	
    }


    void printTree( ostream & out = cout ) const
    {
        if( isEmpty( ) )
            out << "Empty tree" << endl;
        else
            printTree( root, out );

    }

  
    void makeEmpty( )
    {
        makeEmpty( root );
    }

 
    void insert( const Comparable & x )
    {
        insert( x, root );
    }
     

    void insert( Comparable && x )
    {
        insert( std::move( x ), root );
    }
    

    void remove( const Comparable & x )
    {
        remove( x, root );
    }


  private:
    struct BinaryNode
    {
        Comparable element;
        BinaryNode *left;
        BinaryNode *right;
        int Number;
      //每个节点上数据的数量，允许有重复地在树中存储数据
      BinaryNode( const Comparable & theElement, BinaryNode *lt, BinaryNode *rt , int num )
	: element{ theElement }, left{ lt }, right{ rt } , Number{ num } { }
        
      BinaryNode( Comparable && theElement, BinaryNode *lt, BinaryNode *rt , int num )
	: element{ std::move( theElement ) }, left{ lt }, right{ rt }, Number{ num } { }

    };

    BinaryNode *root;



    void insert( const Comparable & x, BinaryNode * & t )
    {
        if( t == nullptr )
	  t = new BinaryNode{ x, nullptr, nullptr, 1 };
        else if( x < t->element )
            insert( x, t->left );
        else if( t->element < x )
            insert( x, t->right );
        else
	  t->Number = t->Number + 1; 
    }
    
  
    void insert( Comparable && x, BinaryNode * & t )
    {
        if( t == nullptr )
	  t = new BinaryNode{ std::move( x ), nullptr, nullptr, 1 };

        else if( x < t->element )
            insert( std::move( x ), t->left );
        else if( t->element < x )
            insert( std::move( x ), t->right );
        else
	  t->Number++; 
    }


    void remove( const Comparable & x, BinaryNode * & t )
    {

        if( t == nullptr )
            return;  
        if( x < t->element )
            remove( x, t->left );
        else if( t->element < x )
            remove( x, t->right );
        else if( t->left != nullptr && t->right != nullptr ) 
        {
            t->element = findMin( t->right )->element;
            remove( t->element, t->right );
        }
        else
        {
	  if(t->Number > 1)
	    {
	      t->Number = t->Number -1;
	      return;
	    }
            BinaryNode *oldNode = t;
            t = ( t->left != nullptr ) ? t->left : t->right;
            delete oldNode;
        }
    }

    BinaryNode * findMin( BinaryNode *t ) const
    {

        if( t == nullptr )
            return nullptr;
        if( t->left == nullptr )
            return t;
        return findMin( t->left );
    }

    BinaryNode * findMax( BinaryNode *t ) const
    {
 
        if( t != nullptr )
            while( t->right != nullptr )
                t = t->right;
        return t;
    }



    bool contains( const Comparable & x, BinaryNode *t ) const
    {
        while( t != nullptr )
            if( x < t->element )
                t = t->left;
            else if( t->element < x )
                t = t->right;
            else
                return true;   

        return false;   
    }



    void makeEmpty( BinaryNode * & t )
    {
        if( t != nullptr )
        {
            makeEmpty( t->left );
            makeEmpty( t->right );
            delete t;
        }
        t = nullptr;
    }

//有序地打印树中的所有元素
    void printTree( BinaryNode *t, ostream & out ) const
    {

        if( t != nullptr )
        {
            printTree( t->left, out );
	    for(int j = 0; j < t->Number ;j++){
            out << t->element << endl;
	    }
	    printTree( t->right, out );
        }
    }


    BinaryNode * clone( BinaryNode *t ) const
    {
        if( t == nullptr )
            return nullptr;
        else
	  return new BinaryNode{ t->element, clone( t->left ), clone( t->right ),t->Number };
    }
};



#endif
